import { DataRPermits } from './../../domain/models/dataRPermits';
import { DataSourceService } from './../../service/dataSourceService';
import { DataRegisterService } from './../../service/dataRegisterService';
import { DataRegister, DataRegisterDataType } from './../../domain/models/dataRegister';
import { ConditionService } from './../../service/conditionService';
import { Guid } from './../../common/utils/guid';
import { ApiDataContext } from './../../domain/apiDataContext';
import { Transaction } from 'tiny-entity2';
import { ServiceException } from './../../common/excpetion/serviceException';
import { Workflow } from './../../domain/models/workflow';
import { ServiceManager } from './../../common/serviceFactory/serviceManager';
import * as Koa from 'koa';
import { route, RouteOptionsVerify, Tools, UsingLock } from "../../common";
import { WorkflowService } from '../../service/workflowService';
import { PathService } from '../../service/pathService';
import md5 = require('md5');
import { FlowNodeService } from '../../service/flowNodeService';
import { DataSource } from '../../domain/models/dataSource';
import { DataRPermitsService } from '../../service/datarPermitsServices';
import * as fs from 'fs';
import xlsx from 'node-xlsx';
import { cRequest } from '../../common/http/cRequest';
import { Condition } from '../../domain/models/condition';
import { SyncWriterServices } from '../../service/syncWirterServices';
import { MongodbHelper } from './../../common/mongodbHelper';
import {Logs} from '../../domain/dataModels/Logs'

const wSvr = ServiceManager.Current.GetService<WorkflowService>('WorkflowService');
const pSvr = ServiceManager.Current.GetService<PathService>('PathService');
const cSvr = ServiceManager.Current.GetService<ConditionService>('ConditionService');
const dSvr = ServiceManager.Current.GetService<DataRegisterService>('DataRegisterService');
const fSvr = ServiceManager.Current.GetService<FlowNodeService>('FlowNodeService');
const dsSvr = ServiceManager.Current.GetService<DataSourceService>('DataSourceService');
const dpSvr = ServiceManager.Current.GetService<DataRPermitsService>('DataRPermitsService');

class WorkflowController {
    @route(new RouteOptionsVerify('GET', '/api/workflow'))
    async queryWorkflows(ctx: Koa.Context, next) {
        let p = ctx.query;
        if (p.isDisable && p.isDisable == 'true') {
            p.isDisable = true;
        }
        else {
            p.isDisable = false;
        }
        ctx.response.body = await wSvr.QueryWorkflow(p);
        await next();
    }
    @route(new RouteOptionsVerify('GET', '/api/workflow/:id'))
    async getWorkflowInfoById(ctx: Koa.Context, next) {
        let { id } = ctx.params;
        const result = await wSvr.GetWorkflowById(id);
        if (!result.workflowNodeItems) {
            result.workflowNodeItems = [];
        }

        if (result.stopConditionId) {
            result.stopCondition = await cSvr.GetConditionById(result.stopConditionId);
        }

        ctx.response.body = result;
        await next();
    }

    @route(new RouteOptionsVerify('GET', '/api/workflow/:id/fields'))
    async getWorkflowFields(ctx: Koa.Context, next) {
        let { id } = ctx.params;

        ctx.response.body = await wSvr.GetWorkflowFields(id);
        await next();
    }

    @route(new RouteOptionsVerify('GET', '/api/workflow/flowNode/:flowNodeId'))
    async getByFlowNodeId(ctx: Koa.Context, next) {
        let { flowNodeId } = ctx.params;
        ctx.response.body = await wSvr.GetWorkflowByFlowNodeId(flowNodeId);
        await next();
    }

    @route(new RouteOptionsVerify('PUT', '/api/workflow/isDisable'))
    async setWorkflowStatus(ctx: Koa.Context, next) {
        let data: Workflow[] = ctx.request.body;
        let lockKey = md5(data.map(x => x.id).sort().join(','));

        await UsingLock(lockKey, async () => {
            return Transaction(new ApiDataContext(), async ctx => {
                for (let wf of data) {
                    if (!wf.id) {
                        throw new ServiceException('未传入id');
                    }
                    let workflow = await wSvr.GetWorkflowById(wf.id);
                    workflow.isDisable = wf.isDisable;
                    workflow.updateTime = Date.now();
                    await wSvr.UpdateWorkflow(workflow, ctx);
                }
            });
        });

        ctx.response.body = {};
        await next();
    }

    @route(new RouteOptionsVerify('POST', '/api/workflow'))
    async saveWorkflow(ctx: Koa.Context, next) {
        let orgId = ctx.headers['orgid'];
        let data: Workflow = ctx.request.body;
        if (!data.id) {
            throw new ServiceException('未传入Id');
        }
        await UsingLock(data.id, async () => {
            await Transaction(new ApiDataContext(), async ctx => {
                let ds = await dsSvr.GetDataSourceByObjectId(data.id);
                let workflowFields = await wSvr.GetWorkflowFields(data.id);
                if (ds) {
                    ds.name = data.name;
                    ds.orgId = orgId;
                    ds.data = workflowFields.map(x => {
                        return {
                            bindField: x,
                            name: x
                        }
                    });
                    await dsSvr.UpdateDataSource(ds, ctx);
                }
                else {
                    ds = new DataSource();
                    ds.id = Guid.GetGuid();
                    ds.objectId = data.id;
                    ds.objectType = 'workflow';
                    ds.dataType = 'workflowOrder';
                    ds.name = data.name;
                    ds.orgId = orgId;
                    ds.data = workflowFields.map(x => {
                        return {
                            bindField: x,
                            name: x
                        }
                    })

                    await dsSvr.CreateDataSource(ds, ctx);
                }

                let entity = await wSvr.GetWorkflowById(data.id);
                if (entity) {
                    entity.name = data.name;
                    entity.updateTime = Date.now();
                    entity.orgId = orgId;
                    entity.refPageId = data.refPageId;
                    if (data.stopConditionId) {
                        entity.stopConditionId = data.stopConditionId;
                        data.stopCondition = await cSvr.GetConditionById(data.stopConditionId);
                    }
                    await wSvr.UpdateWorkflow(entity, ctx);

                    await wSvr.DeleteWorkflowNodeItemBywIdLogic(entity.workflowNodeItems);
                }
                else {
                    data.createTime = Date.now();
                    data.orgId = orgId;
                    data.stopCondition = await cSvr.GetConditionById(data.stopConditionId);
                    await wSvr.CreateWorkflow(data, ctx);
                }

                let childWorkflowDs = new Map<string, string>();
                for (let item of data.workflowNodeItems) {
                    item.workflowId = data.id;
                    item.id = Guid.GetGuid();
                    item.orgId = orgId;
                    if (!item.flowNodeId) {
                        throw new ServiceException('未传入flowNodeId');
                    }
                    // await wSvr.CreateWorkflowNodeItem(item, ctx);

                    let flowNode = await fSvr.GetFlowNodeById(item.flowNodeId);
                    if (flowNode) {
                        flowNode.workflowId = data.id;
                        flowNode.orgId = orgId;
                        await fSvr.UpdateFlowNode(flowNode, ctx);

                        if (flowNode.childrenWorkflowId) {
                            childWorkflowDs.set(flowNode.childrenWorkflowId, flowNode.childrenWorkflowName);
                        }
                    }
                }

                await wSvr.CreateWorkflowNodeItems(data.workflowNodeItems, ctx);

                childWorkflowDs.forEach(async (value, key) => {
                    let ds = await dsSvr.GetDataSourceByObjectId(key);
                    if (ds) {
                        ds.name = data.name + '#' + value;
                        ds.orgId = orgId;
                        ds.data = workflowFields.map(x => {
                            return {
                                bindField: x,
                                name: x
                            }
                        });
                        await dsSvr.UpdateDataSource(ds, ctx);
                    }
                    else {
                        ds = new DataSource();
                        ds.id = Guid.GetGuid();
                        ds.objectId = key;
                        ds.objectType = 'childrenWorkflow';
                        ds.dataType = 'workflowOrder';
                        ds.orgId = orgId;
                        ds.name = data.name + '#' + value;
                        ds.data = workflowFields.map(x => {
                            return {
                                bindField: x,
                                name: x
                            }
                        });

                        await dsSvr.CreateDataSource(ds, ctx, false);
                    }
                });


                await pSvr.DeletePathByWorkflowId(data.id, ctx);
                for (let item of data.paths) {
                    item.id = Guid.GetGuid();
                    item.workflowId = data.id;
                    item.createTime = Date.now();
                    item.orgId = orgId;
                    // await pSvr.CreatePath(item, ctx);

                    if (item.condition) {
                        item.condition.id = Guid.GetGuid();
                        item.condition.objectId = item.id;
                        item.condition.objectType = 'Path';
                        item.condition.orgId = orgId;
                        // await cSvr.CreateCondition(item.condition, ctx);
                    }
                }

                await pSvr.CreatePaths(data.paths, ctx);
                let paths= data.paths.filter(x=>x && x.condition).map(x =>x && x.condition);
                if(paths && paths.length>0)
                {
                    await cSvr.CreateConditions(paths, ctx);
                }
            });
        })
        ctx.response.body = data;
        await next();
    }
    @route(new RouteOptionsVerify('DELETE', '/api/workflow/:id'))
    async deleteWorkflow(ctx: Koa.Context, next) {
        let { id } = ctx.params;
        await UsingLock(id, async () => {
            return await wSvr.DeleteWorkflow(id);
        })

        ctx.response.body = {};
        await next();
    }

    @route(new RouteOptionsVerify('post', '/api/dataRegister'))
    async saveDataRegister(ctx: Koa.Context, next) {
        // let userId = ctx.headers['userid'];
        const orgId = ctx.headers['orgid'];

        let data: {
            dataRegister: DataRegister;
            datarPermits: DataRPermits[]
        } = ctx.request.body;

        await UsingLock(data.dataRegister.id, async () => {
            return await saveDataRegister(data, orgId);
        });

        ctx.response.body = data.dataRegister;
        await next();
    }

    @route(new RouteOptionsVerify('post', '/api/dataRegister/:id/status/:status'))
    async updateDataRegisterStatus(ctx: Koa.Context, next) {
        let { id, status } = ctx.params;

        ctx.response.body = await UsingLock(id, async () => {
            let dataRegister = await dSvr.GetDataRegisterById(id);
            if (status === 'opend') {
                dataRegister.status = null;
            }
            else {
                dataRegister.status = status;
            }


            return await dSvr.UpdateDataRegister(dataRegister);
        });

        await next();
    }

    @route(new RouteOptionsVerify('post', '/api/dataRegister/list'))
    async saveDataRegisterList(ctx: Koa.Context, next) {
        const orgId = ctx.headers['orgid'];
        const dataReq: {
            dataRegister: DataRegister;
            datarPermits: DataRPermits[]
        }[] = ctx.request.body;
        const lockKey = md5(dataReq.map(x => x.dataRegister.id).sort().join(''));

        await UsingLock(lockKey, async () => {
            return await Transaction(new ApiDataContext(), async ctx => {
                for (const item of dataReq) {
                    await saveDataRegister(item, orgId, ctx);
                }
            });
        });

        ctx.response.body = dataReq;

        await next();
    }

    @route(new RouteOptionsVerify('get', '/api/dataRegister/:id'))
    async getDataRegistById(ctx: Koa.Context, next) {
        let { id } = ctx.params;
        ctx.response.body = await dSvr.GetDataRegisterById(id);
        await next();
    }

    @route(new RouteOptionsVerify('delete', '/api/dataRegister/:id'))
    async deleteDataRegistById(ctx: Koa.Context, next) {
        let { id } = ctx.params;
        await UsingLock(id, async () => {
            return await dSvr.DeleteDataRegistr(id);
        })

        let url = `http://filegennet5.zepride.com/YouJianDataSync/DeleteProject?YouJianProjectId=${id}`;
        await cRequest.Current.Get<any>(url);

        ctx.response.body = {};
        await next()
    }

    @route(new RouteOptionsVerify('POST', '/api/dataRegister/import'))
    async importWorkflowOrder(ctx: Koa.Context, next) {
        let reqData: {
            objectType: string;
            objectId: string;
            dataType: string;
        } = ctx.query;

        if (reqData.objectType == 'workflow') {
            reqData.dataType = DataRegisterDataType.WorkflowOrder;
        }

        let orgId = ctx.headers['orgid'];
        let userId = ctx.headers['userid'];
        let result = {
            errors: []
        };
        const file = ctx.request.files.file;
        const reader = fs.createReadStream(file.path);

        let buf = await Tools.streamToBuffer(reader);
        let obj = xlsx.parse(buf);

        await Transaction(new ApiDataContext(), async ctx => {
            let workflow: Workflow;
            if (reqData.objectType == 'workflow') {
                workflow = await wSvr.GetWorkflowById(reqData.objectId);
            }

            for (let i = 1; i < obj[0].data.length; i++) {
                let head: any[] = obj[0].data[0];
                let row: any[] = obj[0].data[i];

                let fields = {};

                for (let index = 0; index < head.length; index++) {
                    fields[head[index]] = row[index];
                }

                let dataRegister = new DataRegister();
                dataRegister.id = Guid.GetGuid();
                dataRegister.objectType = reqData.objectType;
                dataRegister.objectId = reqData.objectId;
                dataRegister.version = Date.now();
                dataRegister.orgId = orgId;
                dataRegister.updateTime = Date.now();
                dataRegister.createTime = Date.now();
                dataRegister.operatorTime = Date.now();
                dataRegister.operatorId = userId;
                dataRegister.dataType = reqData.dataType;

                dataRegister.data = {
                    fields
                }
                if (workflow) {
                    dataRegister.data.workflowId = workflow.id;
                    dataRegister.data.flowNodesItems = workflow.workflowNodeItems.map(x => {
                        return {
                            nodeName: x.flowNode.name,
                            flowNodeId: x.flowNodeId,
                        }
                    });
                }

                await dSvr.CreateDataRegister(dataRegister, ctx);
            }
        });

        ctx.response.body = result;

        await next();
    }
}

const saveDataRegister = async (data: {
    dataRegister: DataRegister;
    datarPermits: DataRPermits[]
}, orgId: string, ctx: ApiDataContext = new ApiDataContext()) => {
    if (!data.dataRegister.id) {
        throw new ServiceException('未传入Id');
    }
    if (!data.dataRegister.objectId) {
        throw new ServiceException('未传入objectId');
    }
    if (!data.dataRegister.objectType) {
        throw new ServiceException('未传入objectType');
    }
    if (!data.dataRegister.data) {
        throw new ServiceException('未传入data');
    }
    if (!data.dataRegister.dataType) {
        throw new ServiceException('未传入dataType');
    }

    if (!data.dataRegister.operatorId) {
        data.dataRegister.operatorId = 'all';
    }

    data.dataRegister.operatorTime = Date.now();

    let hasData = await dSvr.GetDataRegisterById(data.dataRegister.id);
    if (hasData && hasData.version) {
        // 数据库中的数据版本 大于 提交的数据版本 
        if (hasData.version > data.dataRegister.version) {
            throw new ServiceException('当前数据已经被更新，请刷新后重新提交！');
        }
        else {
            hasData.version = Date.now();
        }
    }
  
    SyncWriterServices.Current.AddHandler(async () => {
        new MongodbHelper().insert<Logs>("OperationLog",new Logs(JSON.stringify(data),"saveDataRegister"));
    });

    
    await Transaction(ctx, async ctx => {
        if (data.dataRegister.userIds && data.dataRegister.userIds.length == 0) {
            data.dataRegister.userIds = null;
        }
        if (hasData) {
            hasData.data = data.dataRegister.data;
            if (data.dataRegister.userIds) {
                hasData.userIds = data.dataRegister.userIds;
            }

            hasData.orgId = orgId;
            hasData.status = data.dataRegister.status;
            await dSvr.UpdateDataRegister(hasData, ctx);
        }
        else {
            data.dataRegister.orgId = orgId;
            await dSvr.CreateDataRegister(data.dataRegister, ctx);
        }

        return;
    });
    
    //做一个异步写入队列，进行异步写入。 问题是无法保证事务。
    SyncWriterServices.Current.AddHandler(async () => {
        await Transaction(new ApiDataContext(), async ctx1 => {
            if (data.datarPermits) {
                await dpSvr.DeleteDataRPermitsByDataRegisterId(data.dataRegister.id);
                for (let item of data.datarPermits) {
                    item.id = Guid.GetGuid();
                    item.dataRegisterId = data.dataRegister.id;
                    item.orgId = orgId;
                }

                await dpSvr.CreateDataRPermits(data.datarPermits, ctx1);
            }
        })

    });

    return;
}